/**
 *    Copyright (c) 2021 WANGFEIHU
 *    SSO-PLUS is licensed under Mulan PSL v2.
 *    You can use this software according to the terms and conditions of the Mulan PSL v2.
 *    You may obtain a copy of Mulan PSL v2 at:
 *             http://license.coscl.org.cn/MulanPSL2
 *    THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *    See the Mulan PSL v2 for more details.
 */
package cn.codinglives.ssoplus.controller;

import cn.codinglives.ssoplus.entity.SsoAuth2;
import cn.codinglives.ssoplus.entity.SsoMain;
import cn.codinglives.ssoplus.entity.SsoSaml;
import cn.codinglives.ssoplus.service.JwtService;
import cn.codinglives.ssoplus.service.SsoMainService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.saml.metadata.MetadataManager;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.List;

@Controller
public class SsoController {

	// Logger
	private static final Logger LOG = LoggerFactory
			.getLogger(SsoController.class);

	@Resource
	private MetadataManager metadata;

	@Resource
	private SsoMainService ssoConfService;

	@Resource
	private JwtService jwtService;

	@Value("${sys.sso.domain:localhost}")
	private String domain;

	@RequestMapping(value = "/{tenantCode}/sso/login", method = RequestMethod.GET)
	public ResponseEntity login(HttpServletRequest request, @PathVariable String tenantCode) {
		List<SsoMain> ssoConfList = ssoConfService.list("");
		for (SsoMain main : ssoConfList) {
			if (tenantCode.equalsIgnoreCase(main.getTenantCode())) {
				switch (main.getProtocolType()) {
					case "saml":
						return processSamlLogin(main);
					case "oauth2":
						return processOauthLogin(request, main);
					case "jwt":
						// do jwt
					case "code":
						return process3rdCodeLogin(request, main);
					default:
						break;
				}
			}
		}
		return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("请检查该租户的单点登录配置");
	}

	/**
	 * 第三方code进行登陆。
	 * @param request
	 * @param main
	 * @return
	 */
	private ResponseEntity process3rdCodeLogin(HttpServletRequest request, SsoMain main) {
		String token = getToken(request, "xforce-saas-token");
		String code = request.getParameter("code");
		if (!StringUtils.hasText(code)) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("参数不完整");
		}
		LOG.info("login tenant and code:{}", main.getTenantCode(), code);
		String url = "/code/validation?tenantCode=" + main.getTenantCode() + "&code=" + code;
		return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(url)).build();
	}

	/**
	 * process saml request.
	 * @param main
	 * @return
	 */
	private ResponseEntity processSamlLogin(SsoMain main) {
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if (auth == null)
			LOG.debug("Current authentication instance from security context is null");
		else
			LOG.debug("Current authentication instance from security context: "
					+ this.getClass().getSimpleName());
		if (auth == null || (auth instanceof AnonymousAuthenticationToken)) {
			SsoSaml ssoSaml = ssoConfService.getSsoSamlByMainId(main.getId());
			String idp =  ssoSaml.getEntityId();
			LOG.info("tenant:{} mapped idp:{}", main.getTenantCode(), idp);
			String url ="/saml/login?idp=" + idp;
			return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(url)).build();
		} else {
			LOG.info("The current user is already logged:{}, {}", auth.getName(), auth.getCredentials());
			String url = "/saml2/dispatch?tenantCode=" + main.getTenantCode();
			return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(url)).build();
		}
	}

	/**
	 * oauth2 login.
	 * @param request
	 * @param main
	 * @return
	 */
	private ResponseEntity processOauthLogin(HttpServletRequest request, SsoMain main) {
		String token = getToken(request, "xforce-saas-token");
		SsoAuth2 auth2 = ssoConfService.getSsoAuth2ByMainId(main.getId());
		String authUrl  = auth2.getAuthorizeUrl().replace("{redirect_uri}", domain + "/auth2/back");
		if (StringUtils.isEmpty(token)) {
			return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(auth2.getAuthorizeUrl())).build();
		} else {
			return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(main.getSsoUrl())).build();
		}
	}

	private  String getToken(HttpServletRequest request, String cookieName){
		Cookie[] cookies = request.getCookies();
		String value = null;
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				if (cookieName.equals(cookie.getName())) {
					value = cookie.getValue();
					break;
				}
			}
		}
		return value;
	}

}
